package de.passau.uni.sec.compose.id.core.service.security.uaa;
import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.ClassPathResource;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.RestTemplate;
import com.sun.org.apache.xerces.internal.util.URI;
import com.sun.org.apache.xerces.internal.util.URI.MalformedURIException;
import de.passau.uni.sec.compose.id.common.exception.IdManagementException;
import de.passau.uni.sec.compose.id.common.exception.IdManagementException.Level;
import de.passau.uni.sec.compose.id.core.service.security.TokenResponse;
import de.passau.uni.sec.compose.id.core.service.security.UsersAuthzAndAuthClient;
import de.passau.uni.sec.compose.id.rest.client.HTTPClient;
@Service
public class UAAClient implements UsersAuthzAndAuthClient
{
private static Logger LOG = LoggerFactory.getLogger(UAAClient.class);
private static String oauthAdminToken = null;
private String UAAUrl;
private String password;
private String username;
private String clientId;
private String scope;
private String redirectUriBase;
private String getOauthAdminAuthToken() throws IdManagementException
{
//TODO manage expired admin token!!
// check expiration date of token (Oauth2 library? JWT?)
/*if(this.oauthAdminToken==null)
{*/
TokenResponse res = getClientCredentialsToken(username,password);
this.oauthAdminToken = res.getAccessToken();
/*}*/
return oauthAdminToken;
}
public UAAClient() {
// load properties file from classpath
Properties properties = new Properties();
ClassPathResource resource = new ClassPathResource("uaa.properties");
try {
properties.load(resource.getInputStream());
} catch (IOException e) {
LOG.error("Error while reading uaa properties file.");
e.printStackTrace();
}
this.UAAUrl = properties.getProperty("uaa.url");
this.username = properties.getProperty("client.credentials.admin.username");
this.password = properties.getProperty("client.credentials.admin.pass");
this.clientId = properties.getProperty("compose.client.id");
this.redirectUriBase = properties.getProperty("compose.client.redirect");
}
@Override
public String getUAAUrl() {
return UAAUrl;
}
@Override
public TokenResponse getClientCredentialsToken(String username, String password) throws IdManagementException
{
ResponseEntity<HashMap> responseEntity = null;
HTTPClient<HashMap> http = new HTTPClient<>();
HttpHeaders headers = http.createBasicAuthenticationHttpHeaders(username, password);
headers.add("Content-type","application/x-www-form-urlencoded;charset=utf-8");
MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
map.add("grant_type", "client_credentials");
String url = this.UAAUrl+"/oauth/token";
responseEntity = http.getDataHTTPCall(http.POST_REST, url, null, map,headers, HashMap.class);
TokenResponse tr = new TokenResponse();
Map<String,String> res = responseEntity.getBody();
String val = (res.get("access_token"));
if(val != null)
tr.setAccessToken(val);
val = res.get("jti");
if(val != null)
tr.setJti(val);
val = res.get("scope");
if(val != null)
tr.setToken_scope(val);
val = res.get("token_type");
if(val != null)
tr.setToken_type(val);
return tr;
}
public TokenResponse getImplicitTokenCredentials(String client, String username, String password) throws IdManagementException
{
if(client == null)
client = this.clientId;
HTTPClient<HashMap> http = new HTTPClient<>();
ResponseEntity<HashMap> responseEntity = null;
MultiValueMap<String, String> urlQueryParams = new LinkedMultiValueMap<String, String>();
urlQueryParams.add("client_id",client);
urlQueryParams.add("response_type", "token");
urlQueryParams.add("redirect_uri", this.redirectUriBase+client);
urlQueryParams.add("state", "8658ae7786b51a8aa750abf28815455e");
MultiValueMap<String, String> postData = new LinkedMultiValueMap<>();
postData.add("username", username);
postData.add("password", password);
postData.add("source", "credentials");
HttpHeaders headers = new HttpHeaders();
headers.add("Accept","application/json;charset=utf-8");
TokenResponse tr = new TokenResponse();
String url = this.UAAUrl+"/oauth/authorize";
//process the post request as a Form encoded post message.
responseEntity = http.getDataHTTPCall(http.POST_FORM, url, urlQueryParams, postData, headers, HashMap.class);
if(responseEntity.getStatusCode().equals(HttpStatus.FOUND))
{
URI uri;
try {
uri = new URI(responseEntity.getHeaders().getFirst("Location"));
String fragment = uri.getFragment();
if(fragment != null)
{
String []split = fragment.split("&");
for(String s: split)
{
if(s.contains("access_token"))
tr.setAccessToken(getStringValue(s));
if(s.contains("token_type"))
tr.setToken_type(getStringValue(s));
if(s.contains("scope"))
tr.setToken_scope(getStringValue(s));
if(s.contains("jti"))
tr.setJti(getStringValue(s));
}
if(tr.getAccessToken()!=null)
return tr;
}
} catch (MalformedURIException e) {
throw new IdManagementException("An error ocurred while communicating with an external server ",e,LOG,"An error ocurred trying to parse the uri: "+responseEntity.getHeaders().getFirst("Location")+" after HTTP communication with "+url,Level.ERROR,500);
}
}
else if(responseEntity.getStatusCode().equals(HttpStatus.UNAUTHORIZED))
{
LOG.error( "Incorrect credentials \""+username+"\" and \""+password+"\"");
throw new IdManagementException("Authentication failed, wrong credentials ",null, LOG," Incorrect credentials \""+username+"\" and \""+password+"\"",Level.INFO, 401);
}
throw new IdManagementException("Authentication failed.",null, LOG," Incorrect credentials \""+username+"\" and \""+password+"\"",Level.ERROR, responseEntity.getStatusCode().value());
}
/**
*
* @param s string in the form expression=value
* @return value
*/
private String getStringValue(String s)
{
int pos = s.indexOf("=")+1;
if(pos>0)
{
return s.substring(pos);
}
return null;
}
public OpenIdUserData getOpenIdData(String token) throws IdManagementException
{
ResponseEntity<OpenIdUserData> responseEntity = null;
HTTPClient<OpenIdUserData> http = new HTTPClient<>();
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", "Bearer " + token);
headers.add("Accept","application/json;charset=utf-8");
String url = this.UAAUrl+"/userinfo?schema=openid";
try{
responseEntity = http.getDataHTTPCall(http.GET, url, null, null, headers, OpenIdUserData.class);
}catch(IdManagementException ex)/*MAKE sure to always forward the IdManagementException to avoid losing information.*/
{
throw ex;
}
catch(Exception e)
{
LOG.error("An Exception ocurred trying to parse the uri: "+responseEntity.getHeaders().getFirst("Location")+" after HTTP communication with "+url+ " response: "+responseEntity.toString());
throw new IdManagementException("An error ocurred while getting information for the user",null,LOG,"An error ocurred trying to parse the uri: "+responseEntity.getHeaders().getFirst("Location")+" after HTTP communication with "+url+ " response: "+responseEntity.toString(),Level.INFO,responseEntity.getStatusCode().value());
}
if(!responseEntity.getStatusCode().equals(HttpStatus.OK))
{
LOG.error("Unexpected error code "+responseEntity.getStatusCode()+" trying to parse the uri: "+responseEntity.getHeaders().getFirst("Location")+" after HTTP communication with "+url+ " response: "+responseEntity.toString());
throw new IdManagementException("An error ocurred while getting information for the user",null,LOG,"An error ocurred trying to parse the uri: "+responseEntity.getHeaders().getFirst("Location")+" after HTTP communication with "+url+ " response: "+responseEntity.toString(),Level.INFO,responseEntity.getStatusCode().value());
}
return responseEntity.getBody();
}
@Override
public Map<String, Object> createUser(Serializable userData)
throws IdManagementException {
UAAUserRequest req = (UAAUserRequest) userData;
HTTPClient<HashMap<String,Object>> http = new HTTPClient<>();
ResponseEntity<HashMap> responseEntity = null;
String url = this.UAAUrl+"/Users";
HttpHeaders headers = http.createBasicAuthenticationHttpHeaders(username, password);
LOG.debug("Setting token to:"+getOauthAdminAuthToken());
LOG.info("Retrieving token from UAA successful");
headers.setContentType(MediaType.APPLICATION_JSON);
headers.set("Authorization", "Bearer " +getOauthAdminAuthToken());
headers.add("Accept","application/json;charset=utf-8");
RestTemplate restTemplate = new RestTemplate();
List<HttpMessageConverter<?>> messageConverters = new ArrayList<HttpMessageConverter<?>>();
messageConverters.add(new MappingJackson2HttpMessageConverter());
restTemplate.getMessageConverters().add(new StringHttpMessageConverter());
restTemplate.setMessageConverters(messageConverters);
try{
HttpEntity request = new HttpEntity(req, headers);
responseEntity= restTemplate.exchange(url, HttpMethod.POST, request,
HashMap.class);
}
catch(HttpClientErrorException ce)
{
if(ce.getStatusCode().equals(HttpStatus.CONFLICT))
throw new IdManagementException("Username already exists",null,LOG,"Conflict while attempting to create a user "+url+ " response: "+ce.getResponseBodyAsString(),Level.ERROR,ce.getStatusCode().value());
if(ce.getStatusCode().equals(HttpStatus.UNAUTHORIZED))
throw new IdManagementException("An error ocurred during HTTP communication",ce,LOG,"Admin account in UAA is not authorized to create users!. This shouldn't happen!! HttClientError while attempting to create a User. StatusCode:"+ce.getStatusCode()+ce.getStatusText()+" response Message"+ce.getResponseBodyAsString(),Level.ERROR,500);
throw new IdManagementException("An error ocurred during HTTP communication",ce,LOG,"HttClientError while attempting to create a User. StatusCode:"+ce.getStatusCode()+ce.getStatusText()+" response Message"+ce.getResponseBodyAsString(),Level.ERROR,500);
}
catch(Exception e)
{
throw new IdManagementException("An error ocurred during HTTP communication",e,LOG,"Unknown exception while trying to create a user: Exception"+e.getClass(),Level.ERROR,500);
}
if(!responseEntity.getStatusCode().equals(HttpStatus.CREATED)&&!responseEntity.getStatusCode().equals(HttpStatus.OK))
LOG.error("Error ocurred during creation or a UAA user, status code: "+responseEntity.getStatusCode().value());
return responseEntity.getBody();
}
@Override
public void deleteUser(String userId)
throws IdManagementException {
HTTPClient<HashMap<String,Object>> http = new HTTPClient<>();
ResponseEntity<HashMap> responseEntity = null;
String url = this.UAAUrl+"/Users/"+userId;
HttpHeaders headers = http.createBasicAuthenticationHttpHeaders(username, password);
headers.setContentType(MediaType.APPLICATION_JSON);
headers.set("Authorization", "Bearer " +getOauthAdminAuthToken());
headers.add("Accept","application/json;charset=utf-8");
RestTemplate restTemplate = new RestTemplate();
List<HttpMessageConverter<?>> messageConverters = new ArrayList<HttpMessageConverter<?>>();
messageConverters.add(new MappingJackson2HttpMessageConverter());
restTemplate.getMessageConverters().add(new StringHttpMessageConverter());
restTemplate.setMessageConverters(messageConverters);
try{
HttpEntity request = new HttpEntity(headers);
responseEntity= restTemplate.exchange(url, HttpMethod.DELETE, request,
HashMap.class);
}
catch(HttpClientErrorException ce)
{
if(ce.getStatusCode().equals(HttpStatus.NOT_FOUND))
throw new IdManagementException("User not found",null,LOG,"Attempting to delete a nonexisting user "+url+ " response: "+ce.getResponseBodyAsString(),Level.ERROR,ce.getStatusCode().value());
if(ce.getStatusCode().equals(HttpStatus.UNAUTHORIZED))
throw new IdManagementException("An error ocurred during HTTP communication",ce,LOG,"Admin account in UAA is not authorized to create users!. This shouldn't happen!! HttClientError while attempting to create a User. StatusCode:"+ce.getStatusCode()+ce.getStatusText()+" response Message"+ce.getResponseBodyAsString(),Level.ERROR,500);
throw new IdManagementException("An error ocurred during HTTP communication",ce,LOG,"HttClientError while attempting to create a User. StatusCode:"+ce.getStatusCode()+ce.getStatusText()+" response Message"+ce.getResponseBodyAsString(),Level.ERROR,500);
}
catch(Exception e)
{
throw new IdManagementException("An error ocurred during HTTP communication",e,LOG,"Unknown exception while trying to create a user: Exception"+e.getClass(),Level.ERROR,500);
}
if(!responseEntity.getStatusCode().equals(HttpStatus.OK))
LOG.error("Error ocurred during deletion of a UAA user, status code: "+responseEntity.getStatusCode().value());
}
@Override
public void changePassword(String userToken, String id, String old_password,
String new_password) throws IdManagementException {
UAAUserPasswordRequest pass = new UAAUserPasswordRequest();
pass.setOldPassword(old_password);
pass.setPassword(new_password);
HTTPClient<HashMap<String,Object>> http = new HTTPClient<>();
ResponseEntity<HashMap> responseEntity = null;
String url = this.UAAUrl+"/Users/"+id+"/password";
HttpHeaders headers = http.createBasicAuthenticationHttpHeaders(username, password);
headers.setAccept(Collections.singletonList(new MediaType("application","json")));
headers.set("Authorization", "bearer " +userToken);
headers.add("Content-Type","application/json;charset=utf-8");
headers.setContentType(MediaType.APPLICATION_JSON);
RestTemplate restTemplate = new RestTemplate();
List<HttpMessageConverter<?>> messageConverters = new ArrayList<HttpMessageConverter<?>>();
messageConverters.add(new MappingJackson2HttpMessageConverter());
restTemplate.getMessageConverters().add(new StringHttpMessageConverter());
restTemplate.setMessageConverters(messageConverters);
try{
HttpEntity request = new HttpEntity(pass, headers);
responseEntity= restTemplate.exchange(url, HttpMethod.PUT, request,
HashMap.class);
}
catch(HttpClientErrorException ce)
{
if(ce.getStatusCode().equals(HttpStatus.CONFLICT))
throw new IdManagementException("Conflict while changing password",null,LOG,"Conflict while attempting to change password for user "+url+ " response: "+ce.getResponseBodyAsString(),Level.ERROR,ce.getStatusCode().value());
if(ce.getStatusCode().equals(HttpStatus.UNAUTHORIZED))
{
if(responseEntity!=null && responseEntity.getBody() !=null && responseEntity.getBody().containsKey("description"))
throw new IdManagementException(responseEntity.getBody().get("description").toString(), ce, LOG, "Error while chaning password. Description from UAA"+responseEntity.getBody().get("description").toString(), Level.DEBUG , 401);
throw new IdManagementException("Password change usuccessfull. Not authorized",ce,LOG," Unauthorized to change password for user with id "+id+". StatusCode:"+ce.getStatusCode()+ce.getStatusText()+" response Message"+ce.getResponseBodyAsString(),Level.ERROR,500);
}throw new IdManagementException("An error ocurred during HTTP communication",ce,LOG,"HttClientError while attempting to create a User. StatusCode:"+ce.getStatusCode()+ce.getStatusText()+" response Message"+ce.getResponseBodyAsString(),Level.ERROR,500);
}
catch(Exception e)
{
throw new IdManagementException("An error ocurred during HTTP communication",e,LOG,"Unknown exception while trying to create a user: Exception"+e.getClass(),Level.ERROR,500);
}
if(!responseEntity.getStatusCode().equals(HttpStatus.CREATED)&&!responseEntity.getStatusCode().equals(HttpStatus.OK))
LOG.error("Error ocurred during creation or a UAA user, status code: "+responseEntity.getStatusCode().value());
}
}